#include "stdafx.h"
#include "pb/PNGToBMP32.h"

pb::PBResult pb::PNGToBMP32::_ReadPngFile( const std::wstring &file_name )
{
    PBResult result;

    png_byte header[8]; // 8 is the maximum size that can be checked

    /* open file and test for it being a png */
    FILE *fp;
    errno_t err = _tfopen_s(&fp, file_name.c_str(), L"rb");
    if (err)
        _Abort(L"[read_png_file] File %s could not be opened for reading", file_name);
    fread(header, 1, 8, fp);
    if (png_sig_cmp(header, 0, 8))
        _Abort(L"[read_png_file] File %s is not recognized as a PNG file", file_name);


    /* initialize stuff */
    m_pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

    if (!m_pngPtr) {
        _Abort(L"[read_png_file] png_create_read_struct failed");
    }

    m_infoPtr = png_create_info_struct(m_pngPtr);
    if (!m_infoPtr) {
        _Abort(L"[read_png_file] png_create_info_struct failed");
    }

    if (setjmp(png_jmpbuf(m_pngPtr))) {
        _Abort(L"[read_png_file] Error during init_io");
    }

    png_init_io(m_pngPtr, fp);
    png_set_sig_bytes(m_pngPtr, 8);

    png_read_info(m_pngPtr, m_infoPtr);

    m_width = m_infoPtr->width;
    m_height = m_infoPtr->height;
    m_colorType = m_infoPtr->color_type;
    m_bitDepth = m_infoPtr->bit_depth;
    ATLTRACE("PNG: %d x %d, color type = %d, bit depth = %d\n", m_width, m_height, m_colorType, m_bitDepth);

    m_numberOfPasses = png_set_interlace_handling(m_pngPtr);
    png_read_update_info(m_pngPtr, m_infoPtr);


    /* read file */
    if (setjmp(png_jmpbuf(m_pngPtr))) {
        _Abort(L"[read_png_file] Error during read_image");
    }

    m_rowPointers = (png_bytep*) malloc(sizeof(png_bytep) * m_height);
    for (int y = 0; y < m_height; y++)
        m_rowPointers[y] = (png_byte*) malloc(m_infoPtr->rowbytes);

    png_read_image(m_pngPtr, m_rowPointers);

    fclose(fp);
    return result;
}

pb::PBResult pb::PNGToBMP32::_ProcessFile()
{
    PBResult result;
    if (m_infoPtr->color_type != PNG_COLOR_TYPE_RGBA) {
        _Abort(L"[process_file] color_type of input file must be PNG_COLOR_TYPE_RGBA (is %d)",
               m_infoPtr->color_type);
    }

    for (int y = 0; y < m_height; y++) {
        png_byte* row = m_rowPointers[y];
        for (int x = 0; x < m_width; x++) {
            if (m_colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
                png_byte* ptr = &(row[x * 4]);
                rgb_pixel_t pixel;
                pixel.red = ptr[0];
                pixel.green = ptr[1];
                pixel.blue = ptr[2];
                pixel.alpha = ptr[3];
                bmp_set_pixel(m_bmp, x, y, pixel);
            } else if (m_colorType == PNG_COLOR_TYPE_RGB) {
                png_byte* ptr = &(row[x * 3]);
                rgb_pixel_t pixel;
                pixel.red = ptr[0];
                pixel.green = ptr[1];
                pixel.blue = ptr[2];
                pixel.alpha = 255;
                bmp_set_pixel(m_bmp, x, y, pixel);
            }
        }
    }
    return result;
}

void pb::PNGToBMP32::_Abort( const wchar_t * s, ... )
{
    va_list args;
    va_start(args, s);
    vfwprintf(stderr, s, args);
    fwprintf(stderr, L"\n");
    va_end(args);
    //abort();
}

pb::PBResult pb::PNGToBMP32::Convert( const std::wstring &pngFileName, const std::wstring &bmpFileName )
{
    PBResult result;

    result = _ReadPngFile(pngFileName);
    m_bmp = bmp_create(m_width, m_height, 32);

    result = _ProcessFile();

    bmp_save(m_bmp, bmpFileName.c_str());
    bmp_destroy(m_bmp);

    return result;
}

pb::PNGToBMP32::PNGToBMP32() :
    m_width(0),
    m_height(0),
    m_colorType(0),
    m_bitDepth(0),
    m_pngPtr(nullptr),
    m_infoPtr(nullptr),
    m_numberOfPasses(0),
    m_rowPointers(nullptr),
    m_bmp(nullptr)
{
}

pb::PNGToBMP32::~PNGToBMP32()
{
}
